package com.guo.mixframe.framework.urls

import com.guo.mixframe.framework.annotations.Controller
import com.guo.mixframe.framework.annotations.ResponseBody
import com.guo.mixframe.framework.annotations.RpcController
import com.guo.mixframe.framework.log.LogPrint
import com.guo.mixframe.framework.utils.PackageUtils
import com.guo.mixframe.messages.BaseRequestWrapper
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import kotlin.reflect.KClass
import kotlin.reflect.full.*
import kotlin.reflect.jvm.javaMethod

/**
 * @author  gx
 * @description
 */
class URLMapping {

  companion object {
    fun initMethodUrls() {
      createUrlMappings(Controller::class)
      //映射rpc url
      createUrlMappings(RpcController::class)

      LogPrint.logger.debug("Found ${MAPPINGS.size} controllers.")
    }

    private val METHODS: MutableMap<String, URLRecord> = HashMap()
    private val MAPPINGS: MutableMap<String, MutableList<String>> = HashMap()

    private val EmptyUrls: List<String> = ArrayList();

    private fun createUrlMappings(cls: KClass<out Annotation>) {

      val annotationClass = ResponseBody::class
      val classes = PackageUtils.getClassesByAnnotation("com.guo.mixframe", cls)
      for (klass in classes) {
        val classAnnotation = klass.findAnnotation<Annotation>() ?: continue
        val member = cls.members.find { it.name == "urlType" } ?: continue
        val urlType = member.call(classAnnotation) as Int
        val urls = mutableListOf<String>()
        for (method in klass.memberFunctions) {
          val annotations = method.annotations.filter { it.annotationClass == annotationClass }
          if (annotations.isEmpty()) {
            continue
          }
          val annotation = annotations[0]
          val params = method.valueParameters
          if (params.isEmpty()) {
            continue
          }

          //取方法参数，参数必须是从BaseRequestWrapper继承
          val paramClass = params[0].type.classifier as KClass<*>
          if (!paramClass.isSubclassOf(BaseRequestWrapper::class)) {
            continue
          }

          //取BaseRequestWrapper的泛型参数，如果没有则continue
          val typeParams = method.javaMethod?.genericParameterTypes
          if (typeParams.isNullOrEmpty()) {
            continue
          }

          val typeParam = typeParams[0]

          var actualTypes: Array<Type>? = null
          if (typeParam is ParameterizedType) {
            actualTypes = typeParam.actualTypeArguments
            if (actualTypes.isNullOrEmpty()) {
              continue
            }
          }
          val actualType =
            if (actualTypes != null) {
              (actualTypes[0] as Class<*>).kotlin
            } else {
              Unit::class
            }
          //url转换为小写
          val url = (annotation as ResponseBody).url.lowercase()
          //创建URLRecord并记录到METHODS中
          METHODS[url] = URLRecord(klass, method, actualType, urlType)
          urls.add(url)
        }
        //记录类的所有支持url
        MAPPINGS[klass.qualifiedName!!] = urls
      }
    }

    /**
     * 取class下的所有支持的url
     */
    fun getUrls(className: String): List<String> {
      return MAPPINGS.getOrDefault(className, EmptyUrls)
    }

    /**
     * url是否存在
     */
    fun isUrlValid(url: String): Boolean {
      return METHODS.containsKey(url)
    }

    /**
     * url是否标注有firstCache
     */
    fun isFirstCache(url: String): Boolean {
      val record = METHODS[url] ?: return false

      return record.method.annotations.any {
        (it as ResponseBody).firstCache
      }
    }

    /**
     * url是否标注有忽略ip检查
     */
    fun isIgnore(url: String): Boolean {
      val record = METHODS[url] ?: return false

      return record.method.annotations.any {
        (it as ResponseBody).ignore
      }
    }

    /**
     * 取url对应的请求消息Type
     */
    fun getRequestTypeByUrl(url: String): KClass<*> {
      val record = METHODS[url] ?: return Unit::class

      return record.requestType
    }

    /**
     * 获取url对应的url类型
     */
    fun getUrlTypeByUrl(url: String): Int {
      val record = METHODS[url] ?: return -1

      return record.urlType
    }

    /**
     * 反射调用函数
     */
    suspend fun call(url: String, request: Any?): Any? {
      val record = METHODS.getOrDefault(url, null) ?: return null
      val constructor = record.klass.primaryConstructor ?: return null
      return record.method.callSuspend(constructor.call(), request)
    }
  }


}



